#include "Shooter_PCH.h"
#include "GLSLProgram.h"

/*--------------------------------------------------------------------------*/

GLSLProgram::GLSLProgram(const std::string& filename) :
	m_programHandle(0),
	m_vertHandle(0),
	m_fragHandle(0),
	m_filename(filename)
{
	std::cout << "Creating program: " << filename << std::endl;

	std::cout << "Compiling vertex shader..." << std::endl;
	CompileShader(LoadTextFile(".vert"), m_vertHandle, GL_VERTEX_SHADER);

	std::cout << "Compiling fragment shader..." << std::endl;
	CompileShader(LoadTextFile(".frag"), m_fragHandle, GL_FRAGMENT_SHADER);

	std::cout << "Linking program..." << std::endl;
	LinkProgram();

	std::cout << "Done" << std::endl;
}

/*--------------------------------------------------------------------------*/

GLSLProgram::~GLSLProgram()
{
	glDetachShader(m_programHandle, m_vertHandle);
	glDetachShader(m_programHandle, m_fragHandle);

	glDeleteShader(m_vertHandle);
	glDeleteShader(m_fragHandle);

	glDeleteProgram(m_programHandle);
}

/*--------------------------------------------------------------------------*/

void GLSLProgram::Bind()
{
	glUseProgram(m_programHandle);
}

/*--------------------------------------------------------------------------*/

void GLSLProgram::Unbind()
{
	glUseProgram(0);
}

/*--------------------------------------------------------------------------*/

std::string GLSLProgram::LoadTextFile( const std::string& ext )
{
	std::ifstream file((m_filename + ext).c_str());

	if (file.bad())
	{
		std::cout << "Can not open file: " << m_filename << std::endl;
		return "";
	}

	std::stringstream buffer;
	buffer << file.rdbuf();

	return buffer.str();
}

/*--------------------------------------------------------------------------*/

void GLSLProgram::CompileShader( const std::string& src, unsigned int& handle, GLenum type )
{
	handle = glCreateShader(type);

	const char* cstr = src.c_str();
	int len = src.length();

	glShaderSource(handle, 1, &cstr, &len);
	glCompileShader(handle);

	int status = GL_FALSE;
	glGetShaderiv(handle, GL_COMPILE_STATUS, &status);

	if (status != GL_TRUE)
	{
		int infoLength = 0;
		glGetShaderiv(handle, GL_INFO_LOG_LENGTH, &infoLength);

		char* log = new char[infoLength];
		glGetShaderInfoLog(handle, infoLength, &status, log);

		std::cout << "####################################################" << std::endl;
		std::cout << "FILE: " << m_filename << std::endl << std::endl;
		std::cout << "Shader compilation error: " << std::endl;
		std::cout << std::string(log) << std::endl;
		std::cout << "Shader source:" << std::endl;
		std::cout << src << std::endl;
		
		delete [] log;
	} 
}

/*--------------------------------------------------------------------------*/

void GLSLProgram::LinkProgram()
{
	m_programHandle = glCreateProgram();
	glAttachShader(m_programHandle, m_vertHandle);
	glAttachShader(m_programHandle, m_fragHandle);

	glLinkProgram(m_programHandle);

	int status = GL_FALSE;
	glGetProgramiv(m_programHandle, GL_LINK_STATUS, &status);

	if (status != GL_TRUE)
	{
		int infoLength = 0;
		glGetShaderiv(m_programHandle, GL_INFO_LOG_LENGTH, &infoLength);

		char* log = new char[infoLength];
		glGetShaderInfoLog(m_programHandle, infoLength, &status, log);

		std::cout << "####################################################" << std::endl;
		std::cout << "FILE: " << m_filename << std::endl << std::endl;
		std::cout << "Program linking error: " << std::endl;
		std::cout << std::string(log) << std::endl;
		delete [] log;
	}
}

/*--------------------------------------------------------------------------*/

unsigned int GLSLProgram::GetHandle() const
{
	return m_programHandle;
}

/*--------------------------------------------------------------------------*/
